﻿using System;
using System.Windows;
using System.Windows.Controls;
using wi = System.Windows.Interactivity;

namespace Thinktecture.UIAnnotations.Behaviors
{
    public class TriggerActionFactory : DependencyObject
    {
        static TriggerActionFactory _current;

        static TriggerActionFactory()
        {
            _current = new TriggerActionFactory();
        }

        #region TriggerActionTypeName

        public static readonly DependencyProperty TriggerActionTypeNameProperty =
            DependencyProperty.RegisterAttached("TriggerActionTypeName", typeof(string), typeof(TriggerActionFactory),
            new PropertyMetadata("", OnTriggerActionTypeNamePropertyChanged));

        [AttachedPropertyBrowsableForType(typeof(Control))]
        public static string GetTriggerActionTypeName(UIElement element)
        {
            return (string)element.GetValue(TriggerActionTypeNameProperty);
        }

        public static void SetTriggerActionTypeName(UIElement element, string value)
        {
            element.SetValue(TriggerActionTypeNameProperty, value);
        }

        private static void OnTriggerActionTypeNamePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            CreateTrigger(sender);
        }

        private static void CreateTrigger(DependencyObject sender)
        {
            var typeName = sender.GetValue(TriggerActionTypeNameProperty) as string;
            if (string.IsNullOrEmpty(typeName))
                return;
            var eventName = sender.GetValue(EventNameProperty) as string;
            if (string.IsNullOrEmpty(eventName))
                return;
            var detachTypeName = sender.GetValue(DetachTriggerActionTypeNameProperty) as string;
            if (!string.IsNullOrEmpty(detachTypeName))
                return;

            try
            {
                var actionType = Type.GetType(typeName);
                if (actionType == null)
                    throw new ArgumentException(string.Format("Can't create trigger type \"{0}\"!", typeName));
                var action = Activator.CreateInstance(actionType) as wi.TriggerAction;
                var trigger = new wi.EventTrigger(eventName);
                trigger.Actions.Add(action);
                wi.Interaction.GetTriggers(sender).Add(trigger);
            }
            catch (Exception ex)
            {
                throw new ArgumentException(string.Format("Can't create trigger \"{0}\"!", typeName), ex);
            }
        }

        #endregion
        
        #region EventName

        public static readonly DependencyProperty EventNameProperty =
            DependencyProperty.RegisterAttached("EventName", typeof(string), typeof(TriggerActionFactory),
            new PropertyMetadata("", OnEventNamePropertyChanged));

        [AttachedPropertyBrowsableForType(typeof(Control))]
        public static string GetEventName(UIElement element)
        {
            return (string)element.GetValue(EventNameProperty);
        }

        public static void SetEventName(UIElement element, string value)
        {
            element.SetValue(EventNameProperty, value);
        }

        private static void OnEventNamePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            CreateTrigger(sender);
        }
        
        #endregion

        #region DetachTriggerActionTypeName

        public static readonly DependencyProperty DetachTriggerActionTypeNameProperty =
            DependencyProperty.RegisterAttached("DetachTriggerActionTypeName", typeof(string), typeof(TriggerActionFactory),
            new PropertyMetadata("", OnDetachTriggerActionTypeNamePropertyChanged));

        [AttachedPropertyBrowsableForType(typeof(Control))]
        public static string GetDetachTriggerActionTypeName(UIElement element)
        {
            return (string)element.GetValue(DetachTriggerActionTypeNameProperty);
        }

        public static void SetDetachTriggerActionTypeName(UIElement element, string value)
        {
            element.SetValue(DetachTriggerActionTypeNameProperty, value);
        }

        private static void OnDetachTriggerActionTypeNamePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ((FrameworkElement)sender).Loaded += _current.OnElementLoaded;
        }

        private void OnElementLoaded(object sender, RoutedEventArgs e)
        {
            var element = sender as FrameworkElement;
            element.Loaded -= _current.OnElementLoaded;
            var typeName = element.GetValue(DetachTriggerActionTypeNameProperty) as string;
            if (string.IsNullOrEmpty(typeName))
                return;
            try
            {
                var actionType = Type.GetType(typeName);
                if (actionType == null)
                    throw new ArgumentException(string.Format("Can't create trigger type \"{0}\"!", typeName));

                var triggerList = wi.Interaction.GetTriggers(element);
                foreach (var t in triggerList)
                {
                    foreach (var a in t.Actions)
                    {
                        if (a.GetType().Equals(actionType))
                        {
                            a.Detach();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new ArgumentException(string.Format("Can't create trigger \"{0}\"!", typeName), ex);
            }
        }

        #endregion

    }
}
